有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

JavaSpringWebClient:自动计算主体的HMAC签名并将其作为头传递

在我的Spring启动应用程序中,我使用RestTemplate调用一个WS,其中主体HMAC签名应作为HTTP头提供。为此,我使用了ClientHttpRequestInterceptor。基本上,我做到了:

@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
    try {
        String hmac = Hmac.calculateRFC2104HMAC(body, key);
        request.getHeaders().add("X-Hub-Signature", "sha1=" + hmac);
        return execution.execute(request, body);
    }
    catch (NoSuchAlgorithmException | InvalidKeyException e) {
        throw new IOException(e);
    }
}

现在我想使用WebClient在我的反应式应用程序中实现更好的集成。但我迷失在这个新的反应式API中。如何使用ExchangeFilterFunctionBodyInserter实现这一点?难点在于检索主体、执行签名计算以及随后更新请求

谢谢你的支持


共 (1) 个答案

  1. # 1 楼答案

    对主体进行签名需要以序列化的形式进行,但序列化发生在发送数据之前,因此需要拦截数据

    您可以通过创建自己的编码器(例如,包装现有的Jackson2JsonEncoder),并在构建WebClient时将其作为ExchangeStrategies传递来实现这一点。截获序列化数据后,可以注入头。但是编码器没有对ClientHttpRequest的引用,因此需要在HttpConnector中捕获该对象,并将其传递到SubscriberContext

    这篇博文解释了这个过程:https://andrew-flower.com/blog/Custom-HMAC-Auth-with-Spring-WebClient#s-post-data-signing

    例如,您的WebClient创建步骤可能如下所示,其中MessageCapturingHttpConnector是捕获ClientHttpRequestBodyCapturingJsonEncoder的连接器

    Signer signer = new Signer(clientId, secret);
    MessageSigningHttpConnector httpConnector = new MessageSigningHttpConnector();
    BodyCapturingJsonEncoder bodyCapturingJsonEncoder
        = new BodyCapturingJsonEncoder(signer);
    
    WebClient client
        = WebClient.builder()
                   .exchangeFunction(ExchangeFunctions.create(
                           httpConnector,
                           ExchangeStrategies
                                  .builder()
                                   .codecs(clientDefaultCodecsConfigurer -> {
                                       clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonEncoder(bodyCapturingJsonEncoder);
                                       clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(new ObjectMapper(), MediaType.APPLICATION_JSON));
                                   })
                                   .build()
                   ))
                   .baseUrl(String.format("%s://%s/%s", environment.getProtocol(), environment.getHost(), environment.getPath()))
                   .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                   .build();